﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Microsoft.Win32;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Deployment;
using System.Windows;
using StressTesterCommon;
using System.Windows.Threading;
using System.Threading;

namespace StressClient
{
    class ClientViewModel:INotifyPropertyChanged
    {
        #region NotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string info)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(info));
            }
        }
        #endregion
        #region Constructors
        public ClientViewModel(StressClient Client)
        {
            _Client = Client;
            _ServerMessages = new ObservableCollection<string>();
            _CommandConnect = new RelayCommand(ExecuteConnect) { IsEnabled = true };
            _CommandDisconnect = new RelayCommand(ExecuteDisconnect) { IsEnabled = true };

            _Client.MessageRecieved += _Client_MessageRecieved;
            _Client.OnTestStarted += _Client_OnTestStarted;
            _Client.OnQueryCompleted += _Client_OnQueryCompleted;
            _Client.OnTestAborted += _Client_OnTestAborted;
            _Client.OnDisconnect += _Client_OnDisconnect;
            _Client.OnTestCompleted += _Client_OnTestCompleted;
            _Client.OnTestConnection += _Client_OnTestConnection;
        }       
        #endregion
        #region Members
        private object _LockQueriesCompleted = new object();
        private object _LockQueryErrors = new object();
        private DateTime _TestStarted;
        private DispatcherTimer _TestTimer;
        private string _TestDuration = "00:00:00";
        public string TestDuration
        {
            get { return _TestDuration; }
            set
            {
                _TestDuration = value; OnPropertyChanged("TestDuration");
            }
        }
        private string _Status = "Disconnected";
        public string Status
        {

            get { return _Status; }
            set { if (_Status != value)_Status = value; OnPropertyChanged("Status"); }
        }
        private bool _IsTestRunning;
        public bool IsTestRunning
        {
            get { return _IsTestRunning; }
            set { if (value != _IsTestRunning) { _IsTestRunning = value; OnPropertyChanged("IsTestRunning"); } }
        }

        private StressClient _Client;
        public StressClient Client
        {
            get { return _Client; }
        }

        private int _QueryErrors = 0;
        public  int QueryErrors
        {
            get  { return _QueryErrors; } 
            set { _QueryErrors = value; OnPropertyChanged("QueryErrors"); }
        }
        private int _QueriesCompleted;
        public int CompletedQueries
        {
            get { return _QueriesCompleted; }
            set { _QueriesCompleted = value; OnPropertyChanged("CompletedQueries"); }
        }
        public int TotalQueries
        {
            get 
            {
                if (_Client.TestConfiguration != null)
                {
                    return _Client.TestConfiguration.TotalNumberOfQueries;
                }
                else
                {
                    return 0;
                }
            }
        }

        private ObservableCollection<string> _ServerMessages;
        public ObservableCollection<string> ServerMessages
        {
            get { return _ServerMessages; }
        }
        private bool _IsNotConnecting = true;

        public bool IsNotConnecting
        {
            get { return _IsNotConnecting; }
            set { if (value != _IsNotConnecting) { _IsNotConnecting = value; OnPropertyChanged("IsNotConnecting"); } }
        }


        private bool _IsConnected = false;
        public bool IsConnected
        {
            get { return _IsConnected; }
            set { if (value != _IsConnected) { _IsConnected = value; OnPropertyChanged("IsConnected"); OnPropertyChanged("IsNotConnected"); } }
        }

        public bool IsNotConnected
        {
            get { return !_IsConnected; }
        }
        private int _ServerPort = 1000;

        public int ServerPort
        {
            get { return _ServerPort; }
            set { if (value != _ServerPort) { _ServerPort = value; OnPropertyChanged("ServerPort"); } }
        }
        private string _ServerAddress = "";

        public string ServerAddress
        {
            get { return _ServerAddress; }
            set { if (value != _ServerAddress) { _ServerAddress = value; OnPropertyChanged("ServerAddress"); } }
        }
        #endregion
        #region Command declarations
        private ICommand _CommandConnect;
        public ICommand CommandConnect
        {
            get { return _CommandConnect; }
        }

        private ICommand _CommandDisconnect;
        public ICommand CommandDisconnect
        {
            get { return _CommandDisconnect; }
        }
        #endregion
        #region Command implementations
        private void ExecuteDisconnect()
        {
            _Client.Disconnect();
            IsConnected = false;
            
            Status = "Disconnected";
        }
        private void ExecuteConnect()
        {
            IsNotConnecting = false;
            Status = "Connecting";
            bool Success = _Client.Connect(_ServerAddress, _ServerPort);
            if (!Success)
            {
                Status = "Disconnected";
                MessageBox.Show("Failed to connect to server '" + _ServerAddress + "' at port '" + _ServerPort.ToString() + "'");
                IsConnected = false;
            }
            else
            {
                Status = "Connected";
                IsConnected = true;
            }
            IsNotConnecting = true;
        }
        #endregion
        #region Methods
        private void AddMessage(string Message)
        {
            DateTime Now = DateTime.Now;
            string Time = Now.Hour.ToString().PadLeft(2, '0') + ":" + Now.Minute.ToString().PadLeft(2, '0') + ":" + Now.Second.ToString().PadLeft(2, '0');
            Application.Current.Dispatcher.Invoke(new Action(() => { _ServerMessages.Add(Time + ">" + Message); }));
        }

        #endregion
        #region Events
        void _Client_MessageRecieved(TCPMessage Msg)
        {
            switch (Msg.MessageType)
            {
                case MessageTypes.ServerMessage:
                    AddMessage(Msg.Message);
                    break;
                case MessageTypes.ExecuteTest:
                    AddMessage("Execute test");
                    break;
                case MessageTypes.ClientAbortTest:
                    
                    AddMessage("Abort test");
                    Status = "Aborting test";
                    break;
                case MessageTypes.ClientDisconnect:
                    AddMessage("Disconnect from me");
                    break;
            }
        }
        void _Client_OnTestStarted()
        {
            _TestTimer = new DispatcherTimer();
            _TestTimer.Interval = new TimeSpan(0,0,1);
            _TestTimer.Tick += _TestTimer_Tick;
            _TestStarted = DateTime.Now;
            _TestTimer.Start();
            IsTestRunning = true;
            OnPropertyChanged("TotalQueries");
            CompletedQueries = 0;
            QueryErrors = 0;
            Status = "Started test";
        }
        void _Client_OnTestConnection(bool Result)
        {
            if (Result)
            {
                Status = "Can connect to target";
            }
            else
            {
                Status = "Could not connect to target";
            }
        }
        void _TestTimer_Tick(object sender, EventArgs e)
        {
            TimeSpan T = DateTime.Now- _TestStarted ;
            string Time = T.Hours.ToString().PadLeft(2, '0') +
                ":" + T.Minutes.ToString().PadLeft(2, '0') +
                ":" + T.Minutes.ToString().PadLeft(2, '0');
            this.TestDuration = Time;
        }
        void _Client_OnQueryCompleted(QueryResult Result)
        {
            lock (_LockQueriesCompleted)
            {
                this.CompletedQueries++;
                OnPropertyChanged("CompletedQueries");
            }
            if (Result.ExecuteStatus == QueryExecuteStatus.QueryFailed)
            {
                lock (_LockQueryErrors)
                {
                    QueryErrors++;
                    OnPropertyChanged("QueryErrors");
                }
            }

        }
        void _Client_OnTestCompleted()
        {
            Status = "Test completed";
            CompletedQueries = _Client.TestConfiguration.TotalNumberOfQueries;
            _TestTimer.Stop();
            
        }
        void _Client_OnTestAborted()
        {
            Status = "Test aborted";
            _TestTimer.Stop();
        }
        void _Client_OnDisconnect()
        {
            this.IsConnected = false;
            this.Status = "Disconnected";
        }
        #endregion

    }
}
